
#include <string.h>
#include <sstream>
#include <sys/types.h>
#include <sys/time.h>
#include "Debug.h"
#include "Events.h"
#include "define.h" 
using namespace std;

void g_free_FObject(void * fg)
{
        FObject *p = (FObject *)fg;
        if(p!=NULL)
                delete p;
}

void g_free_FGrid(void * fg)
{
	FGrid *p = (FGrid *)fg;
	if(p!=NULL)
		delete p;
}

Events::Events(const char *name, const char *lib, const char *conf, int isize, float fFactor, int kickout)
{
	m_scName = name;
	m_scLib = lib;
	m_scConf = conf;
        m_iGCSize =  isize;
        m_fGCFactor = fFactor;
        m_iKickout = kickout;
        m_ikickout = kickout;
        m_iUpdateMax = 1000;
        m_iReturnType = 0;
        m_iReturnMaxCounts = 1000;

        m_pCons = NULL;
        g_static_rw_lock_init(&_Lock);
        m_TB = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free_FGrid);
/*
        g_static_rw_lock_init(&_uLock);
        m_TU = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
*/
}

Events :: ~Events()
{
        DEBUG1("release Events RESOURCE!!!");
	if(m_TB != NULL)
		g_hash_table_destroy(m_TB);
/*
	if(m_TU != NULL)
		g_hash_table_destroy(m_TU);
*/
        if(m_pCons != NULL)
             delete m_pCons;

	g_static_rw_lock_free(&_Lock);
//	g_static_rw_lock_free(&_uLock);
}

bool  Events::Initialize()
{
/**
 数据库连接关闭，导致consumer调用失败，从而致使系统重新调用Initialize()，如果多个线程同时执行此函数，就将导致程序崩溃! 
 reload_User_Friends有锁保护，而Initialize只在reload_User_Friends函数中被调用！  
*/
	if(m_pCons != NULL)
	{
		m_pCons->callShutdown();
		delete m_pCons;
	}
	m_pCons = new ConsumerDriver(m_scName.c_str(), m_scLib.c_str() , 1); //RTLD_LAZY);
	if(m_pCons == NULL)
	{
		ERRO << "error to load consumer = "<< m_scLib <<endl;
		return false;
	}
	if(!m_pCons->callInit(m_scName.c_str(), m_scConf.c_str()))
	{
		ERRO << "error to callInit on "<< m_scName <<endl;
		return false;
	}
	INFO<<m_scName<<" callInit successful !"<<endl;
	return true;
}
/*

*/
bool Events::GC(long lt, int size)
{
/*
 调用GC的函数必须加锁！
 */
        GHashTable *rtb = g_hash_table_new(g_str_hash, g_str_equal);
	GHashTableIter iter;
	gpointer keyit, valueit;
        DEBUG2("Kickout = ", m_ikickout);
	g_hash_table_iter_init(&iter, m_TB);
        int ic = 0;
	while (g_hash_table_iter_next(&iter, &keyit, &valueit))
	{
		FGrid *f = (FGrid *)valueit;
		if( f!=NULL &&  f->lasttime < (lt - m_ikickout))
		{
			g_hash_table_insert(rtb, keyit, valueit);
			ic++;
			if(ic >= size)
				break;
		}
	}

/*
 * rtb's size < size, means small users is expire, so turn m_iKickout more small. 
 */
        if(g_hash_table_size(rtb) < (unsigned )size)
		m_ikickout = m_ikickout/2;
        else
		m_ikickout *=2;
	if(m_ikickout > m_iKickout)
		m_ikickout = m_iKickout;

	while(true)
	{  
/*
 can't use "while(g_hash_table_iter_next()){ ... }", because mybe one item in rtb will be remove, this will make g_hash_table_iter_next() to fail!
such as:
 rtb's items = {A, B}
 A's frs = {B, C}
 B's frs = {A, C, D}
 C's frs = {A, B}
 I should remove A from B, and remove A from C, and set B's status='n' and C's status='n', and then remove A.
*/ 
		g_hash_table_iter_init(&iter, rtb); // {A, B}
		if(g_hash_table_iter_next(&iter, &keyit, &valueit))
		{
			FGrid *fs = (FGrid *)valueit;
			if(fs == NULL)
				continue ;  
			GHashTableIter iter2;
			gpointer keyit2, valueit2;
/* I want to release user = iter, but I must get his friends list first, 
 * and then I should remove it from his friends's friends list.
 * in then end, I will remove it from m_TB.
 * such as:
 *   iter = A, fs->owner =='A', fs->frs={B, C}
 */
			DEBUG3((char *)keyit, "' friends size = ", g_hash_table_size(fs->frs));
                        // foreach A's frs :: B, C
			g_hash_table_iter_init(&iter2, fs->frs); 
			while(g_hash_table_iter_next(&iter2, &keyit2, &valueit2))
			{
				/*  f2 = B (C) */
				FGrid *f2 = NULL;
				if(keyit2 != NULL)
					f2 = (FGrid *) g_hash_table_lookup(m_TB, keyit2);
				if(f2!= NULL && strcmp(fs->owner, f2->owner) != 0)
				{
                                        DEBUG5("remove ",(char *)keyit, " from ", (char *)keyit2, "'s friends list and set status='n'");
					g_hash_table_remove(f2->frs, keyit);
					f2->status = 'n';
				}
			}
                        DEBUG3("remove ",(char *)keyit," from rtb");
			g_hash_table_remove(rtb, keyit);   
                        DEBUG3("remove ",(char *)keyit," from m_TB");
			g_hash_table_remove(m_TB, keyit);
		}
                else
                      break;
	}	
        g_hash_table_destroy(rtb);
	return true;
}

int Events :: Get_Users_Graph(char *user1, char *user2,int limit, int offset, conn *c) // char **wbuf, int &wsize, int &wbytes)
{
	int iret = BRUNCE_ERR_SYSTEM;
	g_static_rw_lock_writer_lock(&_Lock); 
        iret =  get_users_graph(user1, user2, limit, offset, c); // wbuf, wsize, wbytes);

        int iSize = g_hash_table_size(m_TB);
        if(iSize > m_iGCSize)
        {
             long lt = time(NULL);
             GC(lt, (int)(m_iGCSize*(1 - m_fGCFactor)));
        }
	g_static_rw_lock_writer_unlock(&_Lock); 
        return iret;
}

/*
定时处理函数，对于超时的用户，系统会放入更新列表中，系统通过定时触发更新这些用户的信息

*/
bool Events::Process_Worklist(long lt)
{
/*
	if(!g_static_rw_lock_writer_trylock(&_uLock))
		return false;

	int iUpdate = g_hash_table_size(m_TU);
	DEBUG3("\t NOW, ", iUpdate, " users need to be update!!!\t");
	g_static_rw_lock_writer_unlock(&_uLock);

	while(iUpdate > 0)
	{
		GHashTableIter iter;
		gpointer keyit, valueit;
		DEBUG2("Kickout = ", m_iKickout);
		g_static_rw_lock_writer_lock(&_Lock);
		g_static_rw_lock_writer_lock(&_uLock);
		g_hash_table_iter_init(&iter, m_TU);
		while (g_hash_table_iter_next(&iter, &keyit, &valueit))
		{
			char *uuser = (char *)keyit;
			FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, uuser);
			if(f1 == NULL)
			{
				DEBUG2(uuser," is not exist, create and reload all!");
				char *fur = (char *)g_strdup(uuser);
				f1 = new FGrid(fur, lt);
				g_hash_table_insert(m_TB, fur, f1);
				bool br = update_User_Friends(fur, f1, lt);
				if(! br )
				{
					g_hash_table_remove(m_TB, uuser);
					f1 = NULL;
				}
			}

                	if(f1 != NULL)
			{
				DEBUG2(uuser, " is timeout, reload them!");
				update_User_Friends(uuser, f1, lt);
			}
                  
			g_hash_table_remove(m_TU, uuser);
			break;
		}
		iUpdate--; 

		g_static_rw_lock_writer_unlock(&_uLock);
		g_static_rw_lock_writer_unlock(&_Lock);
		usleep(5);
	}
*/
	return true;
}

int Events :: Get_User_Friends(char *user, unsigned type, int limit, int offset, conn *c) 
{ // length + retcode + total + counts + { ulen+ user + nlen + name } ... ...
         DEBUG6("limit = ", limit, "; offset = ", offset,"; type = ", type);

	int iret = BRUNCE_ERR_SYSTEM;
	long lt = time(NULL);
	g_static_rw_lock_writer_lock(&_Lock);
	iret = prepare_User_Friends(user, lt);
	g_static_rw_lock_writer_unlock(&_Lock);
        if(iret < BRUNCE_SUCCESS)
               return iret;

	
 	g_static_rw_lock_reader_lock(&_Lock); 
	int iSize = g_hash_table_size(m_TB);
	FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, user);
	if(f1 != NULL)
		iret =  get_users_friends(f1, type, offset, limit, c); // wbuf, wsize, wbytes);
	else
		iret = BRUNCE_ERR_NO_DATA;
	g_static_rw_lock_reader_unlock(&_Lock); 

	if(iSize > m_iGCSize)
	{
		g_static_rw_lock_writer_lock(&_Lock); 
		GC(lt, (int)(m_iGCSize*(1 - m_fGCFactor)));
		g_static_rw_lock_writer_unlock(&_Lock); 
	}
	return iret;
}


int Events :: Get_User_Friends(char *user, int limit, int offset, conn *c) // char **wbuf, int &wsize, int &wbytes)
{ //  all_counts + argc + { ulen+ user + nlen + name + type } ... ...

        DEBUG4("limit = ", limit, "; offset = ", offset);

	int iret = BRUNCE_ERR_SYSTEM;
	long lt = time(NULL);
	g_static_rw_lock_writer_lock(&_Lock);
	iret = prepare_User_Friends(user, lt);
	g_static_rw_lock_writer_unlock(&_Lock);

	if(iret < BRUNCE_SUCCESS)
		return iret;

	g_static_rw_lock_reader_lock(&_Lock); 
	int iSize = g_hash_table_size(m_TB);
	FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, user);
	if(f1 != NULL)
		iret =  get_users_friends(f1, offset, limit, c); // wbuf, wsize, wbytes);
	else
		iret = BRUNCE_ERR_NO_DATA;
	g_static_rw_lock_reader_unlock(&_Lock); 

	if(iSize > m_iGCSize)
	{
		g_static_rw_lock_writer_lock(&_Lock); 
		GC(lt, (int)(m_iGCSize*(1 - m_fGCFactor)));
		g_static_rw_lock_writer_unlock(&_Lock); 
	}
	return iret;
}

int Events :: Get_Users_Value(char *user, conn *c) //char **wbuf, int &wsize, int &wbytes)
{ 
// ulen+ user + nlen + name + type  ... ...
	int iret = BRUNCE_ERR_SYSTEM;

	unsigned ulen = 0;
	unsigned uint_len = sizeof(unsigned);
	unsigned used = 0;
                
        g_static_rw_lock_writer_lock(&_Lock);
	FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, user);
	if(f1 == NULL)
	{
		DEBUG2(user," is not exist, create and reload all!");
		char *fuser = (char *)g_strdup(user);
		long lt = time(NULL);
		f1 = new FGrid(fuser, lt);
		g_hash_table_insert(m_TB, fuser, f1);
		bool br = reload_User_Friends(fuser, f1, lt);          
		if(!br && g_hash_table_size(f1->frs) == 0)
		{
			g_hash_table_remove(m_TB, user);
			f1 = NULL;
		}     
	}
	if(f1 != NULL)
	{
		ulen = strlen(user);
		int vlen = f1->value.length();
		int needsize  = (int)( c->wcurr - c->wbuf + ulen+ vlen + 3*uint_len );
		if(c->wsize < needsize)
		{
			DEBUG1("realloc(*wbuf, wbytes+1)");
                        int curr= c->wcurr - c->wbuf;
			char *new_wbuf = (char *)g_realloc(c->wbuf, c->wsize + 2* needsize);
			if(new_wbuf == NULL){
				ERRO<<"can't malloc for write buffer !"<<endl;
				c->wbytes = 0;
                                c->wcurr = c->wbuf;
				iret = BRUNCE_ERR_SYSTEM;
			}
			else
			{
				c->wsize = c->wsize + 2* needsize;
				c->wbuf = new_wbuf;
                                c->wcurr = c->wbuf + curr;
			}
		}
		if(c->wsize >= needsize)
		{
			bcopy(&ulen, c->wcurr + used, uint_len);
			used += uint_len;
			bcopy(user, c->wcurr + used, ulen);
			used += ulen;
			// vlen + value
			bcopy(&vlen, c->wcurr + used, uint_len);
			used += uint_len;
			bcopy(f1->value.c_str(), c->wcurr + used, vlen);
			used += vlen;
			// type
			int itp = (int)(f1->type);
			bcopy(&itp, c->wcurr + used, uint_len);
			used += uint_len;
		}
	}
	int iSize = g_hash_table_size(m_TB);
	g_static_rw_lock_writer_unlock(&_Lock); 

        c->wbytes += used;
        if(used > uint_len*3)
                iret = BRUNCE_SUCCESS;
	if(iSize > m_iGCSize)
	{
		g_static_rw_lock_writer_lock(&_Lock); 
		GC(time(NULL), (int)(m_iGCSize*(1 - m_fGCFactor)));
		g_static_rw_lock_writer_unlock(&_Lock); 
	}
	return iret;
}


int Events::Reload_User(const char *user)
{ //assert(name != NULL);
DEBUG2(" user = ", user);
        int iret = BRUNCE_ERR_SYSTEM;
	g_static_rw_lock_writer_lock(&_Lock);
	FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, user);
	if(f1 != NULL)
		reload_User_Friends((char *)user, f1, time(NULL));
	g_static_rw_lock_writer_unlock(&_Lock);
	return iret;
}

int Events:: Get_Status(string &status)
{ //assert(name != NULL);
	g_static_rw_lock_reader_lock(&_Lock); 
        int isize = g_hash_table_size(m_TB);
	g_static_rw_lock_reader_unlock(&_Lock); 
        status.clear();
        status.append("STAT user_counts ");
        char buf[128];
        int len = sprintf(buf,"%d\r\n", isize);
        status.append(buf, len);	
	return BRUNCE_SUCCESS;
}

bool Events::Clear()
{
	return true;
}

///////////////////////////////////////////////////////
int Events :: prepare_User_Friends(char *user, long lt)
{ 
	FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, user);
	if(f1 == NULL)
	{
		DEBUG2(user," is not exist, create and reload all!");
		char *fuser = g_strdup(user);
		f1 = new FGrid(fuser, lt);
		g_hash_table_insert(m_TB, fuser, f1);
		bool br = reload_User_Friends(fuser, f1, lt);          
                if(!br && g_hash_table_size(f1->frs) == 0)
		{
			g_hash_table_remove(m_TB, user);
			return BRUNCE_ERR_NO_DATA;
		}     
	}
	else
        {
DEBUG6("lasttime = ", f1->lasttime,"; kickout =", m_iKickout, "; lt-kickout = ", lt - m_iKickout);
		if(f1->status != 'y' || f1->lasttime < (lt - m_iKickout) ){
			DEBUG2(user, "'s friends are not all in frs! reload them!");
			reload_User_Friends(user, f1, lt);
		} 
	}

	return BRUNCE_SUCCESS;
}
/*
bool Events :: update_User_Friends(char *user, FGrid *fs, long lt)
{ // no lock !!!!!!!!!!!!!!
       GHashTable *friends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free_FObject);
	int bok = m_pCons->callUpdate(user, lt, friends);
	if(bok < 0 ) // may db connection is lost!
	{
		Initialize();
		bok = m_pCons->callUpdate(user, lt, friends);
	}
	if(bok > 0)
	{
		fs->status = 'y';
		if(g_hash_table_size(friends) > 0){
			GHashTableIter iter;
			gpointer keyit, valueit;
			g_hash_table_iter_init(&iter, friends);
			while (g_hash_table_iter_next(&iter, &keyit, &valueit))
			{ 
				FObject *fo = (FObject *)valueit;
				if(fo != NULL)
				{
					FGrid *ff = (FGrid *)g_hash_table_lookup(m_TB, (char *)keyit);
					if(ff != NULL){ // means user is exist now!
						DEBUG3((char *)keyit," is exist, set type = ",(int)fo->type);
						ff->lasttime = lt;
                                                ff->value = fo->value;
                                                ff->type = fo->type;
						if(strcmp(user, (char *)keyit) != 0)
							g_hash_table_insert(fs->frs, ff->owner, ff->owner); 
					}
					else
					{ //user is not exist, so create it! but status = 'n'
						DEBUG3((char *)keyit, " is not exist, type = ",(int)(fo->type));
                                                char *fu = g_strdup((char *)keyit);  
						ff = new FGrid(fu, lt);
						ff->value = fo->value;
						ff->type = fo->type;
						g_hash_table_insert(m_TB, fu, ff);
						if(strcmp(user, (char *)keyit) != 0)
							g_hash_table_insert(fs->frs, ff->owner, ff->owner); 
					}
					DEBUG6("add ", ff->owner, " to ", user," ; size=", g_hash_table_size(fs->frs));
				}
			}
		}
                g_hash_table_destroy(friends);
		return true;
	}
        g_hash_table_destroy(friends);
	return false;
}
*/

bool Events :: reload_User_Friends(char *user, FGrid *fs, long lt)
{ // no lock !!!!!!!!!!!!!!
       GHashTable *friends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free_FObject);
	int bok = m_pCons->callFriends(user, friends);
	if( bok < 0 ) // may db connection is lost!
	{
		Initialize();
		bok = m_pCons->callFriends(user, friends);
	} /*
        if(bok == 0)
        {
		g_static_rw_lock_writer_lock(&_uLock);
		if((int) g_hash_table_size(m_TU) < m_iUpdateMax)
			g_hash_table_insert(m_TU, g_strdup(user), NULL);
		g_static_rw_lock_writer_unlock(&_uLock);
        } */
	if(bok > 0)
	{
		fs->status = 'y';
		if(g_hash_table_size(friends) > 0){
			GHashTableIter iter;
			gpointer keyit, valueit;
			g_hash_table_iter_init(&iter, friends);
			while (g_hash_table_iter_next(&iter, &keyit, &valueit))
			{ 
				FObject *fo = (FObject *)valueit;
				if(fo != NULL)
				{
					FGrid *ff = (FGrid *)g_hash_table_lookup(m_TB, (char *)keyit);
					if(ff != NULL){ // means user is exist now!
						DEBUG3((char *)keyit," is exist, set type = ",(int)fo->type);
						ff->lasttime = lt;
                                                ff->value = fo->value;
                                                ff->type = fo->type;
						if(strcmp(user, (char *)keyit) != 0)
							g_hash_table_insert(fs->frs, ff->owner, ff->owner); 
					}
					else
					{ //user is not exist, so create it! but status = 'n'
						DEBUG3((char *)keyit, " is not exist, type = ",(int)(fo->type));
                                                char *fu = g_strdup((char *)keyit);  
						ff = new FGrid(fu, lt);
						ff->value = fo->value;
						ff->type = fo->type;
						g_hash_table_insert(m_TB, fu, ff);
						if(strcmp(user, (char *)keyit) != 0)
							g_hash_table_insert(fs->frs, ff->owner, ff->owner); 
					}
					DEBUG6("add ", ff->owner, " to ", user," ; size=", g_hash_table_size(fs->frs));
				}
			}
		}
                g_hash_table_destroy(friends);
		return true;
	}
        g_hash_table_destroy(friends);
	return false;
}

int Events :: get_users_graph(char *user1, char *user2, int limit, int offset, conn *c)
{ //  depth + items + {item1+item2+item3}: item1=vlen+val+....
  // limit 指的是 item !
/* 此函数已经在外部被锁同步,内部不能有锁！ */

	int iret = BRUNCE_ERR_SYSTEM;
        unsigned ulen = 0;
        unsigned uint_len = sizeof(unsigned);
        unsigned used = 0;
	long lt = time(NULL);
	FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, user1);
	if(f1 == NULL)
	{
/*
 * if user is not exist, then create it and reload friends from db
 */
                DEBUG2(user1, " is not exist in table, reload it and his firends!");
		char *user = g_strdup(user1);
		f1 = new FGrid(user, lt);
		g_hash_table_insert(m_TB, user, f1);
		bool br = reload_User_Friends(user, f1, lt);  
                if( !br && g_hash_table_size(f1->frs) == 0)
                {
                    g_hash_table_remove(m_TB, user1);
                    return BRUNCE_ERR_NO_DATA;
                }     
	} 
	else
        {
DEBUG6("lasttime = ", f1->lasttime,"; kickout =", m_iKickout, "; lt-kickout = ", lt - m_iKickout);
		if(f1->status != 'y')
		{ 
			DEBUG2(user1, "'s friends are not all in frs! reload them!");
			reload_User_Friends(user1, f1, lt);           
		} /*
		else if(f1->lasttime < (lt - m_iKickout)) 
		{
                        DEBUG2(f1->owner,  " is timeout, reload it!");
			g_static_rw_lock_writer_lock(&_uLock);
			if( (int)g_hash_table_size(m_TU) < m_iUpdateMax) 
				g_hash_table_insert(m_TU, g_strdup(f1->owner), NULL);
			g_static_rw_lock_writer_unlock(&_uLock);
		} */
	}
/*
 * user's friends are not all in frs! 
 */
	int idepth = 0;       
	if(g_hash_table_lookup(f1->frs, user2) != NULL)
	{	
                DEBUG1("Yes, find it!");
	//	INFO<<user1<<"->"<<user2<<endl;
                c->wbytes += uint_len;
                iret = BRUNCE_SUCCESS;
		bcopy(&idepth, c->wcurr, uint_len);
		return BRUNCE_SUCCESS;
	}

	FGrid *f2 = (FGrid *)g_hash_table_lookup(m_TB, user2);
	if(f2 == NULL)
	{
                DEBUG2(user2, " is not exist in table, reload it and his firends!");
		char *user = g_strdup(user2);
		f2 = new FGrid(user, lt);
		g_hash_table_insert(m_TB, user, f2);
		bool br = reload_User_Friends(user, f2, lt);          
                if(!br && g_hash_table_size(f2->frs) == 0)
                {
                    g_hash_table_remove(m_TB, user2);
                    return BRUNCE_ERR_NO_DATA;
                }     
	} 
	else
        {
       DEBUG6( "lasttime = ", f2->lasttime,"; kickout =", m_iKickout, "; lt-kickout = ", lt - m_iKickout);
		if(f2->status != 'y'){
			DEBUG2(user2, "'s friends are not all in frs! reload them!");
			reload_User_Friends(user2, f2, lt);   
		} /*
		else if(f2->lasttime < (lt - m_iKickout)) 
		{
			DEBUG2(f2->owner, " is timeout, reload it");
			g_static_rw_lock_writer_lock(&_uLock);
			if((int) g_hash_table_size(m_TU) < m_iUpdateMax) 
				g_hash_table_insert(m_TU, g_strdup(f2->owner), NULL);
			g_static_rw_lock_writer_unlock(&_uLock);
		} */
	}

	int icounts = 0;
        idepth = 1;
        char direct = 'a'; 
	GHashTable *h1 = f1->frs;
	GHashTable *h2 = f2->frs;
	if(g_hash_table_size(h1) > g_hash_table_size(h2))
	{
		h1 = f2->frs;
		h2 = f1->frs;
                direct = 'b';
	}
	GHashTableIter iter;
	gpointer keyit, valueit;
	g_hash_table_iter_init(&iter, h1);
        used = 2*uint_len ; //idepth +icounts + ...
//     	bcopy(&direct, *wbuf + used, 1);
//      used += 1;
	while (g_hash_table_iter_next(&iter, &keyit, &valueit))
	{
		if(g_hash_table_lookup(h2, keyit) != NULL)
		{
                        if(icounts < offset)
                        {
                               icounts ++; 
                               continue;
                        } 
			ulen = strlen((char *)keyit);
                        int needsize = c->wcurr - c->wbuf + used + ulen + uint_len;
			if(c->wsize < (int)(needsize ))
			{
				DEBUG1("realloc(*wbuf, wbytes+1)");
                                int curr = c->wcurr - c->wbuf;
				char *new_wbuf = (char *)g_realloc(c->wbuf, c->wsize + needsize*2);
				if(new_wbuf == NULL){
					ERRO<<"can't malloc for write buffer !"<<endl;
					c->wbytes = 0;
                                        c->wcurr = c->wbuf;
					return  BRUNCE_ERR_SYSTEM;
				}
                                c->wbuf = new_wbuf;
				c->wsize = c->wsize + 2*needsize;
                                c->wcurr = c->wbuf + curr;
			}
			bcopy(&ulen, c->wcurr + used, uint_len);
                        used += uint_len;
			bcopy(keyit, c->wcurr + used, ulen);
                        used += ulen;
			DEBUG7(icounts," : ", user1,"->",(char *)keyit,"->",user2);
                        icounts++;
                        if(used >= MAX_WRITE_BUFFER_LEN)
                            break;
                        if(icounts >= limit)
                            break;
                        if(icounts >= m_iReturnMaxCounts)
                            break; 
		}
	}
	if(icounts > 0)
	{
                iret = BRUNCE_SUCCESS;
		c->wbytes += used;
                DEBUG6("length=", used,"; set idepth=", idepth, "; c->wbytes = ", c->wbytes);
		bcopy(&idepth, c->wcurr  , uint_len);
		bcopy(&icounts, c->wcurr + uint_len, uint_len);
                return iret;
	}
        idepth = 2;
	g_hash_table_iter_init(&iter, h1);
	while (g_hash_table_iter_next(&iter, &keyit, &valueit))
	{
        	FGrid *fg2 = (FGrid *)g_hash_table_lookup(m_TB, keyit);
		if(fg2 == NULL)
			continue; 
DEBUG6("lasttime = ", f2->lasttime,"; kickout =", m_iKickout, "; lt-kickout = ", lt - m_iKickout);
                if(f2->status != 'y'){
                        DEBUG2(f2->owner , "'s friends are not all in frs! reload them!");
                        reload_User_Friends(f2->owner, f2, lt);
                } /*
                else if(f2->lasttime < (lt - m_iKickout))
                {
                        DEBUG2(f2->owner, " is timeout, so reload it");
                        g_static_rw_lock_writer_lock(&_uLock);
			if((int) g_hash_table_size(m_TU) < m_iUpdateMax) 
				g_hash_table_insert(m_TU, g_strdup(f2->owner), NULL);
			g_static_rw_lock_writer_unlock(&_uLock);
                } */

		GHashTableIter iter2;
		gpointer keyit2, valueit2;
                DEBUG4("user = ", (char *)keyit, "; frs size =", g_hash_table_size(fg2->frs)); 
		g_hash_table_iter_init(&iter2, fg2->frs);
		while (g_hash_table_iter_next(&iter2, &keyit2, &valueit2))
		{
			if(g_hash_table_lookup(h2, keyit2) != NULL)
			{
                                if(icounts < offset)
                                {
                                      icounts ++;
                                      continue;
                                } 
				ulen = strlen((char *)keyit);
                                int ulen2 = strlen((char *)keyit2);
				int needsize = (int)( c->wcurr - c->wbuf +  used + ulen + uint_len*2 + ulen2);
				if(c->wsize < needsize)
				{
					DEBUG1("realloc(*wbuf, wbytes+1)");
                                        int curr = c->wcurr - c->wbuf;
					char *new_wbuf = (char *)g_realloc(c->wbuf, c->wsize + needsize*2);
					if(new_wbuf == NULL){
						ERRO<<"can't malloc for write buffer !"<<endl;
						c->wbytes = 0;
                                                c->wcurr = c->wbuf;
						return  BRUNCE_ERR_SYSTEM;
					}
                                        c->wbuf = new_wbuf;
                                        c->wcurr = c->wbuf + curr;
					c->wsize = c->wsize +  2*needsize;
				}
				if(direct == 'a'){
					bcopy(&ulen, c->wcurr + used, uint_len);
					used += uint_len;
					bcopy(keyit, c->wcurr + used, ulen);
					used += ulen;

					bcopy(&ulen2, c->wcurr + used, uint_len);
					used += uint_len;
					bcopy(keyit2, c->wcurr + used, ulen2);
					used += ulen2;
				}
				else{
					bcopy(&ulen2, c->wcurr + used, uint_len);
					used += uint_len;
					bcopy(keyit2, c->wcurr + used, ulen2);
					used += ulen2;

					bcopy(&ulen, c->wcurr + used, uint_len);
					used += uint_len;
					bcopy(keyit, c->wcurr + used, ulen);
					used += ulen;
				}

				DEBUG7(icounts," : ", user1,"->",(char *)keyit,"->",user2);
				icounts++;
				if(used >= MAX_WRITE_BUFFER_LEN)
					break;
				if(icounts >= m_iReturnMaxCounts)
					break;
                                if(icounts >= limit)
                                        break;
			}
		}
        }

	if(icounts > 0)
	{
                iret = BRUNCE_SUCCESS;
		c->wbytes += used;
                DEBUG6("length=", used,"; set idepth=", idepth, "; offset = ", 2*uint_len);
		bcopy(&idepth, c->wcurr , uint_len);
		bcopy(&icounts, c->wcurr + uint_len, uint_len);
                return iret;
	}

        DEBUG4("icounts = ", icounts,"; retcode = ", iret);
        return iret;
}

int Events :: get_users_friends(FGrid *fs, unsigned type, unsigned start, unsigned num, conn *c) // char **wbuf, int &wsize, int &wbytes)
{ // total + counts + {item1+item2+item3} ....
  // item = ulen + user + nlen + value 
	unsigned ulen = 0;
	unsigned uint_len = sizeof(unsigned);
	unsigned used = 0;
        int iret = BRUNCE_ERR_SYSTEM;
	unsigned icounts = 0; //g_hash_table_size(fs->frs);
        unsigned itotal = 0;
      
        DEBUG2("friends size = ", icounts);
	GHashTableIter iter;
	gpointer keyit, valueit;
        used = 2*uint_len ; // total + icounts + ...
	g_hash_table_iter_init(&iter, fs->frs);
	while (g_hash_table_iter_next(&iter, &keyit, &valueit))
	{
                FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, keyit);
DEBUG4("user = ", keyit, "; type = ", (int)f1->type);
                unsigned itt = (unsigned)(f1->type);
                if(  (itt&type) != type)
                        continue;
                itotal++;
                if(itotal < start)
                     continue;                
                if(icounts >= num || icounts >= (unsigned) m_iReturnMaxCounts)
                     continue;
		if(used >= MAX_WRITE_BUFFER_LEN)
			continue;
                 
		ulen = strlen((char *)keyit);
		int vlen = f1->value.length();    

		int needsize = (int) (c->wcurr - c->wbuf + used + ulen+ vlen + 2*uint_len );
		if(c->wsize < needsize  )
		{
			DEBUG1("realloc(*wbuf, wbytes+1)");
                        int curr = c->wcurr - c->wbuf;
			char *new_wbuf = (char *)g_realloc(c->wbuf, c->wsize + 2* needsize );
			if(new_wbuf == NULL){
				ERRO<<"can't malloc for write buffer !"<<endl;
				c->wbytes = 0;
                                c->wcurr = c->wbuf;
				return BRUNCE_ERR_SYSTEM;
			}
			c->wbuf = new_wbuf;
			c->wsize = c->wsize + 2*needsize;
                        c->wcurr = c->wbuf +curr;
		}
                // ulen + user
		bcopy(&ulen, c->wcurr + used, uint_len);     
		used += uint_len;                         
		bcopy(keyit, c->wcurr + used, ulen);     
		used += ulen;
                // vlen + value
		bcopy(&vlen, c->wcurr + used, uint_len);     
		used += uint_len;                         
		bcopy(f1->value.c_str(),  c->wcurr + used, vlen);     
		used += vlen;
                icounts++;
	}
	c->wbytes += used;
	iret = BRUNCE_SUCCESS;
	DEBUG4("wbytes=",c->wbytes, "; iret = ", iret);
	bcopy(&itotal, c->wcurr , uint_len);
	bcopy(&icounts, c->wcurr + uint_len, uint_len);
	return iret;
}


int Events :: get_users_friends(FGrid *fs, unsigned start, unsigned num, conn *c) // char **wbuf, int &wsize, int &wbytes)
{ // all_counts + argc + {item1+item2+item3} ....
  // item = ulen + user + nlen + value + type
	unsigned ulen = 0;
	unsigned uint_len = sizeof(unsigned);
	unsigned used = 0;
        int iret = BRUNCE_ERR_SYSTEM;
	int icounts =g_hash_table_size(fs->frs);
        DEBUG2("friends size = ", icounts);
	GHashTableIter iter;
	gpointer keyit, valueit;
        used = 2*uint_len ; //all_counts + argc ...
	g_hash_table_iter_init(&iter, fs->frs);
        int  iargc = 0;
        if((int)num > m_iReturnMaxCounts)
           num = (unsigned) m_iReturnMaxCounts;
	while (g_hash_table_iter_next(&iter, &keyit, &valueit))
	{
                if(iargc < (int) start)
                      continue;
                if(iargc - start >= num)
                      break;
		ulen = strlen((char *)keyit);
                FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, keyit);
                int vlen = f1->value.length();    
		int needsize = (int) (c->wcurr - c->wbuf +  used + ulen+ vlen + 3*uint_len );
		if(c->wsize < needsize )
		{
			DEBUG1("realloc(*wbuf, wbytes+1)");
                        int curr = c->wcurr - c->wbuf;
			char *new_wbuf = (char *)g_realloc(c->wbuf, c->wsize + 2* needsize);
			if(new_wbuf == NULL){
				ERRO<<"can't malloc for write buffer !"<<endl;
				c->wbytes = 0;
                                c->wcurr = c->wbuf;
				return BRUNCE_ERR_SYSTEM;
			}
                        c->wbuf = new_wbuf;
                        c->wcurr = c->wbuf + curr;
			c->wsize = c->wsize + 2*needsize;
		}
                // ulen + user
		bcopy(&ulen, c->wcurr + used, uint_len);     
		used += uint_len;                         
		bcopy(keyit, c->wcurr + used, ulen);     
		used += ulen;
                // vlen + value
		bcopy(&vlen, c->wcurr + used, uint_len);     
		used += uint_len;                         
		bcopy(f1->value.c_str(), c->wcurr + used, vlen);     
		used += vlen;
                // type

                int itp = (int)(f1->type);
                if(m_iReturnType > 0)
                    itp =  g_hash_table_size(f1->frs);     
                DEBUG6("name = ", (char *)keyit,"; m_iReturnType = ", m_iReturnType,"; return value =",  itp);
		bcopy(&itp, c->wcurr + used, uint_len);     
		used += uint_len;
		if(used >= MAX_WRITE_BUFFER_LEN)
			break;
                iargc++;
	}
	c->wbytes += used;
	iret = BRUNCE_SUCCESS;
        DEBUG2("iargc = ", iargc);  
        iargc = iargc - start;
        if(iargc < 0) iargc = 0; 
	DEBUG8("wbytes=", c->wbytes, "; iret = ", iret, ", counts=", icounts, ", num=", iargc);
	bcopy(&icounts, c->wcurr, uint_len);
	bcopy(&iargc, c->wcurr + uint_len, uint_len);
	return iret;
}

